package qc.module.iot.api.channel;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import qc.common.core.enums.CommunicationChannelTypeEnum;
import qc.common.core.enums.ResourceStatusFlagEnum;
import qc.common.core.enums.iot.IotPacketsDataProtocolEnum;
import qc.common.core.enums.iot.IotPacketsTransmitProtocolEnum;
import qc.module.iot.api.channel.handler.HongDianDdpTransmitChannelInboundHandler;
import qc.module.iot.api.channel.handler.TransparentTransmitChannelInboundHandler;
import qc.module.iot.entity.IotChannel;
import qc.module.iot.service.IotChannelService;
import qc.module.iot.service.IotFramePacketsService;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 通信信道服务端管理类
 *
 * @author QuCheng Tech
 * @since 2025/10/28
 */
@Service
//@RefreshScope //不添加RefreshScope只在启动时从nacos获取配置，nacos中配置更新时不会自动获取
public class CommunicationChannelServers {
    private static final Logger logger = LoggerFactory.getLogger(CommunicationChannelServers.class);

    @Value("${qucheng.iot.packets-send-to-mq:false}")
    //@Value("${qucheng.iot.packets-send-to-mq:true}")
    private boolean packetsSendToMQ;

    //@Value("${telemetry.ports}")
    //private String telemetryPorts;

    @Value("${netty.boss.threads:2}")
    private int bossThreads;

    @Value("${netty.worker.threads:16}")
    private int workerThreads;

    @Value("${netty.max.connections.per.port:500}")
    private int maxConnectionsPerPort;

    @Value("${netty.max.connections.per.ip:50}")
    private int maxConnectionsPerIp;

    @Value("${netty.so.backlog:1024}")
    private int soBacklog;

    @Value("${netty.idle.timeout.seconds:30}")
    private int idleTimeoutSeconds;

    //由于在NettylInboundBaseHandler使用@Autowired注解得到的始终为null（原因未知），通过在此初始化后通过构造方法传入
    @Autowired
    IotFramePacketsService framePacketsService;
    @Autowired
    IotChannelService channelService;

    /**
     * Iot服务端通道信息集合
     */
    private List<IotCommunicationChannel> communicationChannels;

    /**
     * 从数据库配置中读取通信信道信息
     *
     * @return void
     * @author QuCheng Tech
     * @since 2025/11/19
     */
    private void initCommunicationChannelsFromDb() {
        communicationChannels = new ArrayList<>();

        List<IotChannel> channels = channelService.getAll();
        if (channels != null && channels.size() > 0x0) {
            for (IotChannel channel : channels) {
                //判断信道的状态，如果是启用状态才添加到需要启动的信道中
                if (channel.getFlag() == ResourceStatusFlagEnum.NORMAL) {
                    IotCommunicationChannel serverChannel = new IotCommunicationChannel();
                    serverChannel.setId(channel.getId());
                    serverChannel.setName(channel.getName());
                    serverChannel.setChanneltype(channel.getChanneltype());
                    serverChannel.setPort(channel.getPort());
                    serverChannel.setTransmitProtocol(channel.getTransmitprotocol());
                    serverChannel.setDataProtocol(channel.getDataprotocol());

                    communicationChannels.add(serverChannel);
                }
            }
        }
    }

    /**
     * 使用默认的通信信道初始化Iot服务端通道信息集合
     *
     * @return void
     * @author QuCheng Tech
     * @since 2025/11/4
     */
    private void initDefaultCommunicationChannels() {
        communicationChannels = new ArrayList<>();

        int channelId = 0x1;
        //测试固定使用
        IotCommunicationChannel serverChannel1 = new IotCommunicationChannel();
        serverChannel1.setId("chl" + channelId++);
        serverChannel1.setName("TCP65100");
        serverChannel1.setChanneltype(CommunicationChannelTypeEnum.NET_TCP);
        serverChannel1.setPort("65100");
        serverChannel1.setTransmitProtocol(IotPacketsTransmitProtocolEnum.TRANSPARENT);
        //serverChannel1.setDataProtocol(IotPacketsDataProtocolEnum.SL651_HEX);
        serverChannel1.setDataProtocol(IotPacketsDataProtocolEnum.SL651_ASCII);
        communicationChannels.add(serverChannel1);

        IotCommunicationChannel serverChannel2 = new IotCommunicationChannel();
        serverChannel2.setId("chl" + channelId++);
        serverChannel2.setName("TCP65110");
        serverChannel2.setChanneltype(CommunicationChannelTypeEnum.GPRS_TCP);
        //serverChannel2.setChanneltype(CommunicationChannelTypeEnum.NET_TCP);
        serverChannel2.setPort("65110");
        serverChannel2.setTransmitProtocol(IotPacketsTransmitProtocolEnum.DDP);
        //serverChannel2.setTransmitProtocol(IotPacketsTransmitProtocolEnum.TRANSPARENT);
        serverChannel2.setDataProtocol(IotPacketsDataProtocolEnum.SL651_ASCII);
        communicationChannels.add(serverChannel2);

        IotCommunicationChannel serverChannel3 = new IotCommunicationChannel();
        serverChannel3.setId("chl" + channelId++);
        serverChannel3.setName("TCP42700");
        //serverChannel3.setChanneltype(CommunicationChannelTypeEnum.GPRS_TCP);
        serverChannel3.setChanneltype(CommunicationChannelTypeEnum.NET_TCP);
        serverChannel3.setPort("42700");
        //serverChannel3.setTransmitProtocol(IotPacketsTransmitProtocolEnum.DDP);
        serverChannel3.setTransmitProtocol(IotPacketsTransmitProtocolEnum.TRANSPARENT);
        serverChannel3.setDataProtocol(IotPacketsDataProtocolEnum.SL_427);
        communicationChannels.add(serverChannel3);

        IotCommunicationChannel serverChannel4 = new IotCommunicationChannel();
        serverChannel4.setId("chl" + channelId++);
        serverChannel4.setName("TCP65200");
        serverChannel4.setChanneltype(CommunicationChannelTypeEnum.NET_TCP);
        serverChannel4.setPort("65200");
        serverChannel4.setTransmitProtocol(IotPacketsTransmitProtocolEnum.TRANSPARENT);
        serverChannel4.setDataProtocol(IotPacketsDataProtocolEnum.MODBUS_LJ_EMS);
        communicationChannels.add(serverChannel4);

        IotCommunicationChannel serverChannel5 = new IotCommunicationChannel();
        serverChannel5.setId("chl" + channelId++);
        serverChannel5.setName("TCP65201");
        serverChannel5.setChanneltype(CommunicationChannelTypeEnum.NET_TCP);
        serverChannel5.setPort("65201");
        serverChannel5.setTransmitProtocol(IotPacketsTransmitProtocolEnum.TRANSPARENT);
        serverChannel5.setDataProtocol(IotPacketsDataProtocolEnum.CUSTOM_DEHONG_SWJ);
        communicationChannels.add(serverChannel5);
    }

    @PostConstruct
    public void startAllCommunicationChannelServers() {
        logger.debug("startAllCommunicationChannelServers 开始启动所有通信信道服务端");

        //初始化服务端通道：解析出每个通道的TCP/UDP、端口号、传输协议、数据协议
        //先从数据库中读取信道配置，如果读取失败或者没有配置信道使用默认
        try {
            initCommunicationChannelsFromDb();
        } catch (Exception ex) {
            logger.debug("startAllCommunicationChannelServers 从数据库中读取通信信道配置异常，" + ex.getMessage());
        }
        if (communicationChannels == null || communicationChannels.size() < 0x1)
            initDefaultCommunicationChannels();

        //如果通道有配置逐个进行启动
        if (communicationChannels != null && communicationChannels.size() > 0x0) {
            logger.info("startAllCommunicationChannelServers 共有通信信道[{}]个", communicationChannels.size());

            for (IotCommunicationChannel communicationChannel : communicationChannels) {
                String channelId = communicationChannel.getId();
                String channelName = communicationChannel.getName();
                CommunicationChannelTypeEnum channelType = communicationChannel.getChanneltype();
                String port = communicationChannel.getPort();
                String param = communicationChannel.getParam();
                IotPacketsTransmitProtocolEnum transmitProtocol = communicationChannel.getTransmitProtocol();
                IotPacketsDataProtocolEnum dataProtocol = communicationChannel.getDataProtocol();

                logger.debug("startAllCommunicationChannelServers 开始启动通信信道服务端，信道名称[{}]，信道类型[{}]，端口号[{}]，参数[{}]，传输协议[{}]，数据协议[{}]",
                        channelName, channelType, port, param, transmitProtocol, dataProtocol);

                //根据信道类型进行不同的服务端启动
                if (channelType != CommunicationChannelTypeEnum.NET_TCP && channelType != CommunicationChannelTypeEnum.NET_UDP &&
                        channelType != CommunicationChannelTypeEnum.GPRS_TCP && channelType != CommunicationChannelTypeEnum.GPRS_UDP) {
                    //不是使用网络通道接收
                    logger.error("startAllCommunicationChannelServers 信道[{}]不是网络通讯，暂时无法启动服务端", channelName);
                } else {
                    boolean isUdp = false;
                    if (channelType == CommunicationChannelTypeEnum.NET_UDP || channelType == CommunicationChannelTypeEnum.GPRS_UDP)
                        isUdp = true;
                    //转换端口号为数字
                    int portNumber = 0x0;
                    try {
                        portNumber = Integer.parseInt(port);
                    } catch (NumberFormatException ex) {
                        logger.error("startAllCommunicationChannelServers 信道[{}]使用的端口号[{}]无法转换为有效的数字，暂时无法启动服务端", channelName, port);
                    }

                    //bossGroup就是parentGroup，是负责处理TCP/IP连接的
                    EventLoopGroup bossGroup = new NioEventLoopGroup(bossThreads);
                    //workerGroup就是childGroup,是负责处理Channel(通道)的I/O事件
                    EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);

                    ServerBootstrap bootstrap = new ServerBootstrap();
                    int finalPortNumber = portNumber;
                    boolean finalIsUdp = isUdp;
                    bootstrap.group(bossGroup, workerGroup)
                            .channel(NioServerSocketChannel.class)
                            .option(ChannelOption.SO_BACKLOG, soBacklog)
                            .option(ChannelOption.SO_REUSEADDR, true)
                            .childOption(ChannelOption.SO_KEEPALIVE, true)
                            .childOption(ChannelOption.TCP_NODELAY, true)
                            .childHandler(new ChannelInitializer<SocketChannel>() {
                                @Override
                                protected void initChannel(SocketChannel socketChannel) {
                                    // 添加空闲状态检测
                                    socketChannel.pipeline().addLast(
                                            new IdleStateHandler(idleTimeoutSeconds, 0, 0, TimeUnit.SECONDS));
                                    //根据不同的传输协议使用不同的handler
                                    switch (transmitProtocol) {
                                        case TRANSPARENT:
                                            socketChannel.pipeline().addLast(
                                                    new TransparentTransmitChannelInboundHandler(framePacketsService, channelService, packetsSendToMQ,
                                                            channelId, channelName, channelType, finalIsUdp, finalPortNumber, dataProtocol));

                                            logger.debug("透明传输协议childHandler 信道名称[{}]，信道类型[{}]，端口号[{}]，参数[{}]，传输协议[{}]，数据协议[{}]",
                                                    channelName, channelType, port, param, transmitProtocol, dataProtocol);

                                            break;
                                        case DDP:
                                            socketChannel.pipeline().addLast(
                                                    new HongDianDdpTransmitChannelInboundHandler(framePacketsService, channelService, packetsSendToMQ,
                                                            channelId, channelName, channelType, finalIsUdp, finalPortNumber, dataProtocol));

                                            logger.debug("宏电DDP传输协议childHandler 信道名称[{}]，信道类型[{}]，端口号[{}]，参数[{}]，传输协议[{}]，数据协议[{}]",
                                                    channelName, channelType, port, param, transmitProtocol, dataProtocol);

                                            break;
                                        default:
                                            // 协议未实现
                                            logger.warn("未实现的传输协议 信道名称[{}]，信道类型[{}]，端口号[{}]，参数[{}]，传输协议[{}]，数据协议[{}]",
                                                    channelName, channelType, port, param, transmitProtocol, dataProtocol);

                                            break;
                                    }
                                }
                            });

                    //ChannelFuture future = bootstrap.bind(port).sync();
                    //绑定端口，bootstrap.bind可能会抛出InterruptedException，进行捕获处理
                    ChannelFuture future = null;
                    try {
                        //可以绑定多个端口
                        future = bootstrap.bind(portNumber).sync();
                    } catch (InterruptedException e) {
                        logger.error("startAllCommunicationChannelServers 信道[{}]绑定服务端端口异常，异常信息[{}]", channelName, e.getMessage());
                    }

                    if (future.isSuccess()) {
                        logger.info("startAllCommunicationChannelServers [{}]通信信道服务端启动成功-OK。信道类型[{}]，端口号[{}]，参数[{}]，传输协议[{}]，数据协议[{}]",
                                channelName, channelType, port, param, transmitProtocol, dataProtocol);
                    } else {
                        logger.error("startAllCommunicationChannelServers [{}]通信信道服务端启动失败-FAIL。信道类型[{}]，端口号[{}]，参数[{}]，传输协议[{}]，数据协议[{}]，失败信息[{}]",
                                channelName, channelType, port, param, transmitProtocol, dataProtocol, future.cause().getMessage());

                        //关闭线程组
                        bossGroup.shutdownGracefully();
                        workerGroup.shutdownGracefully();
                    }

                    //成功绑定到端口之后,给channel增加一个管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程。
                    //2025-11-5，实测不能添加监听器后代码会阻塞，不继续往下执行，导致只有第一个通信信道启动成功，后面的均因为代码未执行没有启动
                    //try {
                    //    future.channel().closeFuture().sync();
                    //} catch (InterruptedException ex) {
                    //    logger.error("startAllCommunicationChannelServers [{}]通信信道服务端启动成功后添加监听器异常。信道类型[{}]，端口号[{}]，参数[{}]，传输协议[{}]，数据协议[{}]，异常信息[{}]",
                    //            name, channelType, port, param, transmitProtocol, dataProtocol, ex.getMessage());
                    //}
                }
            }
        } else {
            logger.error("startAllCommunicationChannelServers 配置的通信信道为空，未启动任何接收服务端口");
        }
    }

    /**
     * 停止所有Netty服务器
     */
    @PreDestroy
    public void stopAllCommunicationChannelServers() {
        logger.debug("startAllCommunicationChannelServers 停止所有通信信道服务端");
    }
}